package com.tools.cleanmaster.controller;

import android.content.Context;

import com.tools.cleanmaster.bean.ApkFileInfo;
import com.tools.cleanmaster.manager.ApkFileManager;
import com.tools.cleanmaster.model.TrashType;
import com.tools.cleanmaster.model.item.TrashItem;
import com.tools.cleanmaster.utils.FeatureConfig;
import com.tools.cleanmaster.utils.LogHelper;
import com.tools.cleanmaster.utils.OptimizerFile;
import com.tools.cleanmaster.utils.TrashUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by lianzhan on 16/2/22.
 */
public class FileRootScanner extends TrashScanner {
    private static final int FOLDER_COUNT = 15;// 超过15个文件夹是空的就不扫了
    private static final int FILE_COUNT = 30;// 超过30个文件扫不到APK,LOG,TMP, 就不扫了
    private static final int FILE_COUNT_NEW_POLICY = 10;//新策略，文件夹中先抽样 10个文件检查。
    /**
     * 需要扫描的全部类型
     **/
    private final ArrayList<TrashType> mTrashTypes = new ArrayList<TrashType>();
    /**
     * 实时存储扫描到的垃圾量
     **/
    private final Map<TrashType, Long> mTypeSizeMap = new HashMap<TrashType, Long>();
    private ArrayList<FileTrashMatcher> mAllmatchers = new ArrayList<FileRootScanner.FileTrashMatcher>();
    private FileTrashMatcher mHighFrequencyMatcher = null;
    private final String[] mSdCardRootDirs;
    private String mCurRootDir;
    private long lAllSearchDirs = 0;//统计递归的目录数

    public FileRootScanner(Context context, TrashHandler handler, TrashType[] types) {
        super(context, handler);
        this.mSdCardRootDirs = handler.getRootDirs();
        if (mSdCardRootDirs == null || mSdCardRootDirs.length == 0) {
            throw new IllegalArgumentException("没SD卡，不扫了");
        }

        if (types == null || types.length == 0) {
            throw new IllegalArgumentException("未传入合适类型");
        }
        for (TrashType type : types) {
            mTrashTypes.add(type);

            if (TrashType.APK_FILE == type) {
                mAllmatchers.add(new ApkMatcher());
            }

            mTypeSizeMap.put(type, 0l);
        }
    }

    private abstract class FileTrashMatcher {
        abstract boolean onMatch(File f);
    }

    @Override
    public void scan(int increaseProgress) {
        scanRoots(increaseProgress);
        for (TrashType t : mTrashTypes) {
            if (t == TrashType.APK_FILE || t == TrashType.LARGE_FILE || t == TrashType.LOG_FILE
                    || t == TrashType.TEMP_FILE) {
                mTrashHandler.onTrashTypeFinish(t, mTypeSizeMap.get(t));
            }
        }
    }

    /**
     * @param increaseProgress 比如说整个扫描过程是100% apk扫描20%那么这个increaseProgress等于20
     **/
    private void scanRoots(int increaseProgress) {

        // 如果强行停止就返回，不更新进度
        if (mStopRequested) {
            if (FeatureConfig.DEBUG_LOG) {
                LogHelper.i(SUB_TAG, "doScan, mStopRequested, have not start,fileScanner return");
            }
            return;
        }

        if (FeatureConfig.DEBUG_LOG) {
            LogHelper.i(SUB_TAG, "doScan, scanRoots begin");
        }

        // 如果没有sd卡就直接更新进度
        if (!TrashUtils.hasSdcard(mSdCardRootDirs)) {
            for (TrashType t : mTrashTypes) {
                mTrashHandler.onTrashTypeFinish(t, mTypeSizeMap.get(t));
            }
            mTrashHandler.updateProgress(increaseProgress, null);
            return;
        }

        // 如果没有需要扫描的内容就直接更新进度
        if (mAllmatchers.size() == 0) {
            if (FeatureConfig.DEBUG_LOG) {
                LogHelper.v(SUB_TAG, "doScan, no matcher, fileScanner return");
            }
            // nothing to be scanned
            mTrashHandler.updateProgress(increaseProgress, null);
            for (TrashType type : mTrashTypes) {
                if (type == TrashType.APK_FILE || type == TrashType.LARGE_FILE || type == TrashType.LOG_FILE
                        || type == TrashType.TEMP_FILE) {
                    mTrashHandler.onTrashTypeFinish(type, 0);
                }
            }
            return;
        }

        final int N = mSdCardRootDirs.length;
        if (N == 0) {
            mTrashHandler.updateProgress(increaseProgress, null);
        }
        int[] progress = calculateProgressByChildsCount(null, mSdCardRootDirs, increaseProgress);
        for (int i = 0; i < N; i++) {
            scanApkByEnumDiskOneCard(mSdCardRootDirs[i], progress[i]);
        }
    }

    private void scanApkByEnumDiskOneCard(String curRootDir, int progress) {
        mCurRootDir = curRootDir;
        ArrayList<TargetFolderParam> targetFolderParams = new ArrayList<TargetFolderParam>();
        TargetFolderParam rootFolderParam = new TargetFolderParam(curRootDir, 4, FILE_COUNT, FOLDER_COUNT);
        rootFolderParam.setIsRootDir(true);

        // 添加根目录下的忽略路径，这些路径不扫描
        for (String aMIgnoreFolderParam : mIgnoreFolderParam) {
            String pathString = curRootDir + aMIgnoreFolderParam;
            File file = new File(pathString);
            if (file.exists()) {
                rootFolderParam.addIgnoreDir(pathString);
            }
        }

        for (int i = 0; i < mTargetFolderParam.length; i++) {
            TargetFolderParam freguentPathFolderParam = mTargetFolderParam[i].copy();
            freguentPathFolderParam.mStrPath = curRootDir + freguentPathFolderParam.mStrPath;
            File frequentFile = new File(freguentPathFolderParam.mStrPath);
            if (frequentFile.exists()) {
                targetFolderParams.add(freguentPathFolderParam);
            }
        }

        targetFolderParams.add(rootFolderParam);
        int N = targetFolderParams.size();
        for (int i = 0; i < N; i++) {
            int progressSubFiles = calculateProgress(progress, i, N);
            TargetFolderParam target = targetFolderParams.get(i);
            if (FeatureConfig.DEBUG_LOG) {
                LogHelper.v(SUB_TAG, "scanDir ItemRoot=" + target.mStrPath);
            }

            scanFile(target, new OptimizerFile(target.mStrPath), 1,
                    progressSubFiles, true);

        }
    }

    /**
     * 先扫描targetFoler的fileLimit个文件，如果发现有垃圾就也扫描该文件夹下面的全部文件和文件夹
     * 如果当前目录下只有文件夹没有文件，那么如果文件夹数目操作folderLimit个，就不扫了，否则扫全部
     **/
    private boolean scanFile(TargetFolderParam targetFolder, File currentFile, int currentlevel,
                             int increaseProgress, boolean needIgnore) {
        String currentFilePath = TrashUtils.getSubDirLower(currentFile.getAbsolutePath(), mSdCardRootDirs);
        boolean emptyFolder = false;
        if (FeatureConfig.DEBUG_LOG) {
            LogHelper.v(
                    SUB_TAG,
                    "scanDir level= " + currentlevel + ", name = " + currentFilePath
                            + " , needIgore = " + needIgnore + ", is folder ="
                            + currentFile.isDirectory());
        }
        if (mStopRequested) {
            mTrashHandler.updateProgress(increaseProgress, currentFile.getAbsolutePath());
            return emptyFolder;
        }
        if (currentFile.isDirectory()) {
            // 判断是否继续递归扫描，判断依据是，当前是否为第5层级
            if (currentlevel > targetFolder.mScanLevel) {
                mTrashHandler.updateProgress(increaseProgress, currentFile.getAbsolutePath());
                return emptyFolder;
            }

            // 其下面的只文件夹是否有可能有忽略路径
            boolean childNeedIgnore = false;
            if (needIgnore) {
                ArrayList<String> igNoreList = targetFolder.mIgnoreDirsArrayList;
                for (String string : igNoreList) {
                    // 如果当前路径是某个忽略路径的一部分，那么其下面的文件夹有忽略路径
                    if (string.startsWith(currentFilePath)) {
                        // 如果当前路径等于忽略路径，那么直接返回
                        if (string.equals(currentFilePath)) {
                            mTrashHandler.updateProgress(increaseProgress, currentFile.getAbsolutePath());
                            return emptyFolder;
                        } else {
                            childNeedIgnore = true;
                            break;
                        }
                    }
                }
            }
            lAllSearchDirs += 1;
            File[] subFiles = currentFile.listFiles();
            int fileLimit = targetFolder.mFileLimit;
            int progressNeedAdd = increaseProgress;

            // 如果当前扫描的根目录下的一级路径，则一级路径全部检查
            if (targetFolder.mIsRootDir && currentlevel == 1) {
                fileLimit = Integer.MAX_VALUE;
            }

            if (subFiles != null && subFiles.length > 0) {
                // 当前文件夹不是空文件夹
                int fileCount = 0;
                int emptyFolderCount = 0;
                final int N = subFiles.length;

                for (int i = 0; i < N; i++) {
                    if (mStopRequested) {
                        return emptyFolder;
                    }
                    int progressSubFiles = calculateProgress(increaseProgress, i, N);
                    File file = subFiles[i];
                    if (file.isFile() && fileCount < fileLimit) {
                        boolean isTrash = matchAllType(file);
                        fileLimit = isTrash ? Integer.MAX_VALUE : fileLimit;
                        mTrashHandler.updateProgress(progressSubFiles, file.getAbsolutePath());
                        progressNeedAdd -= progressSubFiles;
                        fileCount++;
                    } else if (file.isDirectory() && (emptyFolderCount < targetFolder.mFolderLimit
                                                              || currentlevel == 1 && targetFolder.mIsRootDir)) {
                        boolean isEmpty = scanFile(targetFolder, subFiles[i],
                                currentlevel + 1, progressSubFiles,
                                childNeedIgnore);
                        if (isEmpty) {
                            emptyFolderCount += 1;
                        }
                        progressNeedAdd -= progressSubFiles;
                    } else {
                        continue;
                    }
                }

                // 补齐进度
                if (progressNeedAdd > 0) {
                    mTrashHandler.updateProgress(progressNeedAdd, subFiles[N - 1].getAbsolutePath());
                }
            } else {// 当前文件夹是空文件夹
                emptyFolder = true;// 上层路径里的文件夹全部扫描
                mTrashHandler.updateProgress(increaseProgress, currentFile.getAbsolutePath());// 更新进度
                return emptyFolder;
            }
        } else {// currentFile是文件
            matchAllType(currentFile);
            mTrashHandler.updateProgress(increaseProgress, currentFile.getAbsolutePath());
        }
        return emptyFolder;
    }

    //-----------------------以下全盘扫描的比较器们----------------
    private boolean matchAllType(File f) {
        //文件路径统一变小写
        String path = TrashUtils.getSubDirLower(f.getAbsolutePath(), mSdCardRootDirs);
        f = new File(path);

        //快速的 高频matcher
        if (null != mHighFrequencyMatcher) {
            if (mHighFrequencyMatcher.onMatch(f)) {
                return true;
            }
        }
        //遍历 所有matcher，更新高频matcher
        for (int i = 0; i < mAllmatchers.size(); i++) {
            FileTrashMatcher matcherItem = mAllmatchers.get(i);
            if (matcherItem == mHighFrequencyMatcher) {
                continue;
            }
            if (matcherItem.onMatch(f)) {
                mHighFrequencyMatcher = matcherItem;
                return true;
            }
        }
        return false;
    }

    private final class ApkMatcher extends FileTrashMatcher {
        @Override
        boolean onMatch(File f) {
            String absolutePath = f.getAbsolutePath();
            if (absolutePath.endsWith(".apk")) {
                for (String p : APK_PATH_SKIPPED) {
                    if (absolutePath.startsWith(new File(mCurRootDir, p).getAbsolutePath())) {
                        return false;
                    }
                }
                createTrashItemApk(absolutePath);
                return true;
            } else {
                return false;
            }
        }
    }

    /**
     * 创建一个apk的垃圾，并且把这个垃圾扔到，trashsData里，所有的垃圾都会在创建的时候实时的扔进去
     *
     * @param absolutePath 路径
     *
     * @return 创建成功
     **/
    private boolean createTrashItemApk(String absolutePath) {
        if (!mStopRequested) {
            TrashItem trash = null;
            ApkFileInfo apkInfo = ApkFileManager.getInstance().getApkInfo(absolutePath);
            if (apkInfo != null) {
                trash = apkInfo.createApkFileItem();
            }
            if (trash == null) {
                return false;
            }

            if (!mStopRequested) {
                // 过滤重复数据
                mTypeSizeMap.put(TrashType.APK_FILE, mTypeSizeMap.get(TrashType.APK_FILE)
                        + trash.size);
                mTrashesData.putTrash(TrashType.APK_FILE, trash);
            }
            if (FeatureConfig.DEBUG_LOG) {
                LogHelper.i(SUB_TAG, "创建了一个Apk垃圾=" + trash);
            }
            return true;
        } else {
            return false;
        }
    }

    //-----------------------以下为路径的配置信息-----------------------------
    public static final String[] APK_PATH_SKIPPED = {
            ".android_secure",
            "openrecovery",
            "titaniumbackup",
            "titaniumbackup",
            "androidOptimizer/systembackup/"
    };

    /**
     * 忽略路径
     **/
    private String[] mIgnoreFolderParam = {
            "/dcim",
            "/android/data",
            "/tencent",
            "/download",
            "/bluetooth",
            "/games/com.mojang/minecraftworlds",
            //"/baidu/searchbox/books",
            //"/baidu/flyflow/novel",
            "/cloudagent/cache/dropbox",
            "/tapatalk4/cache/longterm",
            "/cloudagent/cache/root",
            //"/baidu/appSearch",
            "/.thumbnails"
    };

    /**
     * 垃圾频繁出现路径
     **/
    private TargetFolderParam[] mTargetFolderParam = {
            new TargetFolderParam("/.android_msg/update/msg/apk", 1), // .android_msg/update/msg/apk
            new TargetFolderParam("/android/data", 2, Integer.MAX_VALUE, Integer.MAX_VALUE), // Android/data with
            new TargetFolderParam("/binco/download/dn-android.qbox.me/android/soft", 1),
            // binco/download/dn-android.qbox.me/android/soft
            new TargetFolderParam("/center.com.eg.android.AlipayGphone/cmd/download", 1),
            // center.com.eg.android.AlipayGphone/cmd/download
            new TargetFolderParam("/download", 3, Integer.MAX_VALUE, Integer.MAX_VALUE), // download
            new TargetFolderParam("/bluetooth", 3, Integer.MAX_VALUE, Integer.MAX_VALUE), // bluetooth
            new TargetFolderParam("/htcmarket/app/data/apk", 3), // htcmarket/app/data/apk
            new TargetFolderParam("/htcmarket/app/data/apk/003/002/001", 1), // htcmarket/app/data/apk/003/002/001
            new TargetFolderParam("/lesyncDownload/data/com.lenovo.leos.cloud.sync/apps/SUSDownload", 1),
            // LesyncDownload/data/com.lenovo.leos.cloud.sync/apps/SUSDownload
            new TargetFolderParam("/PandaHome2/myphone/mybackup/app"), // PandaHome2/myphone/mybackup/app
            new TargetFolderParam("/sina/weibo/SinaAppMarket/apk", 1), // sina/weibo/SinaAppMarket/apk
            new TargetFolderParam("/system/apk/common/apk", 1), // System/APK/COMMON/apk
            new TargetFolderParam("/system/apk/ru/apk", 1), // System/APK/RU/apk
            new TargetFolderParam("/tencent", 3), // tencent
            new TargetFolderParam("/tencent/opensdk/logs", 10), // tencent/opensdk/logs
            new TargetFolderParam("/games/com.mojang/minecraftworlds", 10), // games/com.mojang/minecraftworlds
            new TargetFolderParam("/tencent/cloudsdk/logs", 10), // tencent/cloudsdk/logs
            new TargetFolderParam("/tencent/wns/logs", 10), // tencent/wns/logs
            new TargetFolderParam("/tencent/tmassistantsdk/download", 2), // tencent/tmassistantsdk/download
            new TargetFolderParam("/tencent/qqhd/plugin/market/apk", 1), // tencent/qqhd/plugin/market/apk
            new TargetFolderParam("/tencent/msflogs/com/tencent/mobileqq", 1), // tencent/msflogs/com/tencent/mobileqq
            new TargetFolderParam("/android/data/com.linecorp.lgrgs/cache", 6), // android/data/com.linecorp.lgrgs/cache
            new TargetFolderParam("/android/data/com.linecorp.lgstage/cache", 6),
            // android/data/com.linecorp.lgstage/cache
            new TargetFolderParam("/android/data/cn.wps.moffice_eng/.cache", 6),
            // android/data/cn.wps.moffice_eng/.cache
            //new TargetFolderParam("/baidu/searchbox/books", 10), // baidu/searchbox/books
            //new TargetFolderParam("/baidu/flyflow/novel", 10), // baidu/flyflow/novel
            new TargetFolderParam("/cloudagent/cache/dropbox", 10), // cloudagent/cache/dropbox
            new TargetFolderParam("/tapatalk4/cache/longterm", 10), // tapatalk4/cache/longterm
            new TargetFolderParam("/cloudagent/cache/root", 10), // cloudagent/cache/root
            //new TargetFolderParam("/baidu/appSearch/downloads", 1) // cloudagent/cache/root
    };
}
